#pragma once
#include "dvo.h"
#include <queue>


class Subject{ 
friend class Op;
public:
  Subject() {}
  Subject(Subject&& peer) {}
  Subject& operator=(dvo::ps::Plug<Subject>& input)
  {
    std::promise<Subject> promise;
    std::function<void(Subject&)> fnc = [&](Subject& value) {promise.set_value(std::move(value));};
    dvo::ps::Socket<Subject> termin;
    termin.Bind(fnc);
    //termin.Bind(std::bind<void(Subject)>(&std::promise<Subject>::set_value, &promise, std::placeholders::_1));
    input >> &termin;
    std::future<Subject> result = promise.get_future();
    result.get(); 
    return *this;
  }
};

enum op_inputs_t{ X, Y };
class Op : public dvo::ps::Joiner<op_inputs_t, Subject, Subject>{
public:
  Op() { Add(op_inputs_t::X); Add(op_inputs_t::Y); }
protected:
  virtual void Process() override   
  {
  Subject result;
  //Subject& result = Get(op_inputs_t::Y); 
   //time consuming operation...
  std::this_thread::sleep_for(std::chrono::seconds(5)); 
  Send(result); 
  //*this >> *op_pool.Input(); 
  }

};


dvo::ps::Plug<Subject>& operator+(dvo::ps::Plug<Subject>& x, dvo::ps::Plug<Subject>& y){
  Op* result = new Op(); //an object pool is better
  x >> result->Inputs(op_inputs_t::X);
  y >> result->Inputs(op_inputs_t::Y);
  return *result->Output();
}

//leaf operation
dvo::ps::Plug<Subject>& operator+(Subject& x, Subject& y){
  Op* result = new Op();
  std::function<void(Subject&,Op*,op_inputs_t)> func = [](Subject& p, Op* op, op_inputs_t inpt) {p >> *op->Inputs(inpt);};
  std::async(std::launch::async, func, std::ref(x), result, op_inputs_t::X);
  std::async(std::launch::async, func, std::ref(y), result, op_inputs_t::Y);
  return *result->Output();
}

bool test_po()
{
  return true;
}

void run_po()
{
  Subject A,B,C,D,E,F,G,H;
  std::cout << "A+B+C+D+E+F+G+H sequentially takes 40s to perform." << std::endl;
  Subject result;
  unsigned int start = clock();
  result = ((A+B) + (C+D)) + ((E+F) + (G+H));
  std::cout << "Operation is done in : " << clock()-start << " milliseconds " << std::endl;
}